﻿using System;
using System.Diagnostics;
using Loki.Engine;
using Loki.UI.Wpf.Resources;
using Loki.Utils;

namespace Loki.UI.Wpf
{
    public class SpringWPFEngine : SpringEngine, ITemplatingEngine
    {
        private static SpringContext _TemplatesContext;

        private const string CONTEXTE_TEMPLATES_NAME = "UITemplates";

        public SpringWPFEngine()
            : base()
        {
            _TemplatesContext = new SpringContext(CONTEXTE_TEMPLATES_NAME);
        }

        /// <summary>
        /// Loads the templates definitions.
        /// </summary>
        /// <param name="P_TemplatesDefinitions">The templates definitions.</param>
        public void LoadTemplates(params string[] P_TemplatesDefinitions)
        {
            _TemplatesContext.LoadContext(P_TemplatesDefinitions);
            _TemplatesContext.EndLoad();
        }

        /// <summary>
        /// Gets the view template for the specified viewmodel.
        /// </summary>
        /// <typeparam name="TView">The type of the view.</typeparam>
        /// <param name="P_ViewModel">The view model.</param>
        public TView GetView<TView>(IViewModel P_ViewModel) where TView : IView
        {
            TView L_View = (TView)GetTemplatedView(P_ViewModel);
            MarkForClean(L_View);
            return L_View;
        }

        /// <summary>
        /// Gets the view template for the specified viewmodel.
        /// </summary>
        /// <typeparam name="TView">The type of the view.</typeparam>
        /// <param name="P_ViewModel">The viewmodel.</param>
        /// <param name="P_ModelID">The p_ model ID.</param>
        public TView GetView<TView>(IViewModel P_ViewModel, string P_ModelID) where TView : IView
        {
            TView L_View = (TView)GetTemplatedView(P_ViewModel, P_ModelID);
            MarkForClean(L_View);
            return L_View;
        }

        private IView GetTemplatedView(IViewModel P_Model, string P_ModelID = null)
        {
            IView L_View = null;
            Type L_TemplatedType = P_Model.GetType();
            if (P_ModelID == null)
            {
                while (L_TemplatedType != null)
                {
                    if (_TemplatesContext.IsDefined(L_TemplatedType.Name))
                    {
                        L_View = _TemplatesContext.GetObject<IView>(L_TemplatedType.Name);
                        L_View.ViewModel = P_Model;
                        break;
                    }
                    else
                    {
                        L_TemplatedType = L_TemplatedType.BaseType;
                    }
                }
            }
            else if (_TemplatesContext.IsDefined(P_ModelID))
            {
                L_View = _TemplatesContext.GetObject<IView>(P_ModelID);
                L_View.ViewModel = P_Model;
            }

            if (L_View == null)
            {
                throw BuildErrorFormat<ApplicationException>(ErrorMessages.Engine_Templates_NoViewForType, P_Model.GetType().Name);
            }

            return L_View;
        }

        [Conditional("DEBUG")]
        private void MarkForClean<TViewType>(TViewType P_View) where TViewType : IView
        {
            WeakReference L_Reference = new WeakReference(P_View);
            P_View.Disposed += (s, e) => { Clean(L_Reference); };
        }

        [Conditional("DEBUG")]
        private void Clean(WeakReference P_View)
        {
            IView L_View = P_View.Target as IView;
            if (L_View != null)
            {
                WeakReference L_ViewModelReference = new WeakReference(L_View.ViewModel);
                WeakReference L_ViewReference = new WeakReference(L_View);

                L_View.ViewModel.Dispose();
                L_View.ViewModel = null;

                L_View.Dispose();
                L_View = null;

                GC.Collect();
                LokiServices.Event.RemoveCollectedEntries();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                if (L_ViewModelReference.IsAlive)
                {
                    Log.DebugFormat(LogStrings.Engine_Clean_ViewModelNotDisposed, L_ViewModelReference.Target.GetType());
                }

                if (L_ViewReference.IsAlive)
                {
                    Log.DebugFormat(LogStrings.Engine_Clean_ViewNotDisposed, L_ViewReference.Target.GetType());
                }
            }
        }
    }
}
